﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace JN.Utility 
{
    /// <summary>
    /// 定义New直角坐标系统在Old直角坐标系统的位置关系,实现两个坐标系统之间的坐标变换. 
    /// </summary>
    /// <remarks></remarks>
    public class Coordinate
    {
        private double _offsetX;
        private double _offsetY;
        private double _rotation;
        private bool _isMirrorX;
        private bool _isMirrorY;
        private double _scaleX;
        private double _scaleY;

        public Coordinate(Coordinate originCoord)
        {
            _isMirrorX = originCoord._isMirrorX;
            _isMirrorY = originCoord._isMirrorY;
            _offsetX = originCoord._offsetX;
            _offsetY = originCoord._offsetY;
            _rotation = originCoord._rotation;
            _scaleX = originCoord._scaleX;
            _scaleY = originCoord._scaleY;
        }

        /// <summary>
        /// 指定与旧坐标系统的关系，创建新坐标系统.
        /// </summary>
        /// <param name="xOffset">新坐标系统原点X(相对于旧坐标系统)</param>
        /// <param name="yOffset">新坐标系统原点Y(相对于旧坐标系统)</param>
        public Coordinate(double xOffset, double yOffset)
            : this(xOffset, yOffset, 0, false, false, 1.0, 1.0)
        { }

        /// <summary>
        /// 指定与旧坐标系统的关系，创建新坐标系统.
        /// </summary>
        /// <param name="xOffset">新坐标系统原点X(相对于旧坐标系统)</param>
        /// <param name="yOffset">新坐标系统原点Y(相对于旧坐标系统)</param>
        /// <param name="rotation">新坐标系统相对于旧坐标系统的旋转角度,以弧度为单位,逆时针为正,顺时针为负</param>       
        public Coordinate(double xOffset, double yOffset, double rotation)
            : this(xOffset, yOffset, rotation, false, false, 1.0, 1.0)
        { }

        /// <summary>
        /// 指定与旧坐标系统的关系，创建新坐标系统.
        /// </summary>
        /// <param name="xOffset">新坐标系统原点X(相对于旧坐标系统)</param>
        /// <param name="yOffset">新坐标系统原点Y(相对于旧坐标系统)</param>
        /// <param name="isMirrorX">true,新坐标系统X向与旧坐标系统X向反向,false 同向</param>
        /// <param name="isMirrorY">true,新坐标系统X向与旧坐标系统Y向反向,false 同向</param>       
        public Coordinate(double xOffset, double yOffset, bool isMirrorX, bool isMirrorY)
            : this(xOffset, yOffset, 0, isMirrorX, isMirrorY, 1.0, 1.0) 
        { }


        /// <summary>
        /// 指定与旧坐标系统的关系，创建新坐标系统.
        /// </summary>
        /// <param name="xOffset">新坐标系统原点X(相对于旧坐标系统)</param>
        /// <param name="yOffset">新坐标系统原点Y(相对于旧坐标系统)</param>
        /// <param name="rotation">新坐标系统相对于旧坐标系统的旋转角度,以弧度为单位,逆时针为正,顺时针为负</param>
        /// <param name="isMirrorX">true,新坐标系统X向与旧坐标系统X向反向,false 同向</param>
        /// <param name="isMirrorY">true,新坐标系统X向与旧坐标系统Y向反向,false 同向</param>
        public Coordinate(double xOffset, double yOffset, double rotation, bool isMirrorX, bool isMirrorY)
            : this(xOffset, yOffset, rotation, isMirrorX, isMirrorY, 1.0, 1.0)
        { }


        /// <summary>
        /// 指定与旧坐标系统的关系，创建新坐标系统.
        /// </summary>
        /// <param name="xOffset">新坐标系统原点X(相对于旧坐标系统)</param>
        /// <param name="yOffset">新坐标系统原点Y(相对于旧坐标系统)</param>
        /// <param name="rotation">新坐标系统相对于旧坐标系统的旋转角度,以弧度为单位,逆时针为正,顺时针为负</param>
        /// <param name="isMirrorX">true,新坐标系统X向与旧坐标系统X向反向,false 同向</param>
        /// <param name="isMirrorY">true,新坐标系统X向与旧坐标系统Y向反向,false 同向</param>
        /// <param name="scaleX">X向缩放比例系数,表示新坐标系统中X向scanX个单位相当于旧坐标系统中X向1个单位</param>
        /// <param name="scaleY">Y向缩放比例系数,表示新坐标系统中Y向scanY个单位相当于旧坐标系统中Y向1个单位</param> 
        public Coordinate(double xOffset, double yOffset, double rotation, bool isMirrorX, bool isMirrorY, double scaleX, double scaleY)
        {
            if (scaleX <= 0 || scaleY <= 0)
            {
                throw new ArgumentOutOfRangeException("scaleX <= 0 || scaleY <= 0"); 
            }
            _offsetX = xOffset;
            _offsetY = yOffset;
            _rotation = rotation;
            _isMirrorX = isMirrorX;
            _isMirrorY = isMirrorY;
            _scaleX = scaleX;
            _scaleY = scaleY;
        }

        /// <summary>
        /// 定义旧座标系统相对于新座标系统的关系.
        /// </summary>
        /// <param name="coorRelation"></param>
        public Coordinate(CoordinateRelation coorRelation)
            :this(coorRelation.XOffset,coorRelation.YOffset,coorRelation.Rotation,coorRelation.IsMirrorX,coorRelation.IsMirrorY,coorRelation.ScaleX,coorRelation.ScaleY) 
        { 
            
        }

        public CoordinateRelation CoorRelation
        {
            get 
            { 
                CoordinateRelation relation =  new CoordinateRelation();
                relation.XOffset = _offsetX;
                relation.YOffset = _offsetY;
                relation.IsMirrorX = _isMirrorX;
                relation.IsMirrorY = _isMirrorY;
                relation.ScaleX = _scaleX;
                relation.ScaleY = _scaleY;
                relation.Rotation = _rotation; 
                return relation; 
            }
        }
 
        public PointD PointOld2New(PointD pInOldCoor)
        {
            pInOldCoor.Offset(-_offsetX, -_offsetY);
            pInOldCoor.Rotation(-_rotation);
            pInOldCoor.Mirror(_isMirrorX, _isMirrorY);
            pInOldCoor.Scale(_scaleX, _scaleY);
            return pInOldCoor;
        }

        public PointD PointNew2Old(PointD pInNewCoor)
        {
            pInNewCoor.Scale(1 / _scaleX, 1 / _scaleY);
            pInNewCoor.Mirror(_isMirrorX, _isMirrorY);
            pInNewCoor.Rotation(_rotation);
            pInNewCoor.Offset(_offsetX, _offsetY);

            return pInNewCoor;
        }

        public SizeD SizeOld2New(SizeD sizeInOldCoor)
        { 
            sizeInOldCoor.Rotation(-_rotation);
            sizeInOldCoor.Mirror(_isMirrorX, _isMirrorY);
            sizeInOldCoor.Scale(_scaleX, _scaleY);
            return sizeInOldCoor;
        }

        public SizeD SizeNew2Old(SizeD sizeInNewCoor)
        {
            sizeInNewCoor.Scale(1 / _scaleX, 1 / _scaleY);
            sizeInNewCoor.Mirror(_isMirrorY, _isMirrorY);
            sizeInNewCoor.Rotation(_rotation); 

            return sizeInNewCoor;
        }

       

        public RectangleD RectOld2New(RectangleD rect)
        {
            PointD p1, p2, p3, p4;
            p1 = rect.Location;
            p2 = new PointD(rect.Location.X + rect.Width, rect.Location.Y);
            p3 = new PointD(rect.Location.X + rect.Width, rect.Location.Y + rect.Height);
            p4 = new PointD(rect.Location.X, rect.Location.Y + rect.Height);

            PointD[] pArray = new PointD[4];
            pArray[0] = PointOld2New(p1);
            pArray[1] = PointOld2New(p2);
            pArray[2] = PointOld2New(p3);
            pArray[3] = PointOld2New(p4);

            Double minX = Double.MaxValue;
            Double minY = Double.MaxValue;
            Double maxX = Double.MinValue;
            Double maxY = Double.MinValue;

            foreach (PointD p in pArray)
            {
                minX =  Math.Min(p.X, minX);
                minY =  Math.Min(p.Y, minY);
                maxX =  Math.Max(p.X, maxX);
                maxY =  Math.Max(p.Y, maxY);
            }

            RectangleD rectInNewCoor = new RectangleD(minX, minY, maxX - minX, maxY - minY);

            return rectInNewCoor;
        }

        public RectangleD RectNew2Old(RectangleD rect)
        {
            PointD p1, p2, p3, p4;
            p1 = rect.Location;
            p2 = new PointD(rect.Location.X + rect.Width, rect.Location.Y);
            p3 = new PointD(rect.Location.X + rect.Width, rect.Location.Y + rect.Height);
            p4 = new PointD(rect.Location.X, rect.Location.Y + rect.Height);

            PointD[] pArray = new PointD[4];
            pArray[0] = PointNew2Old(p1);
            pArray[1] = PointNew2Old(p2);
            pArray[2] = PointNew2Old(p3);
            pArray[3] = PointNew2Old(p4);

            Double minX = Double.MaxValue;
            Double minY = Double.MaxValue;
            Double maxX = Double.MinValue;
            Double maxY = Double.MinValue;

            foreach (PointD p in pArray)
            {
                minX =  Math.Min(p.X, minX);
                minY =  Math.Min(p.Y, minY);
                maxX =  Math.Max(p.X, maxX);
                maxY =  Math.Max(p.Y, maxY);
            }

            RectangleD rectInOldCoor = new RectangleD(minX, minY, maxX - minX, maxY - minY);
            return rectInOldCoor;
        }
         
    }

    public struct CoordinateRelation
    {
        private double _xOffset;
        private double _yOffset;
        private double _rotation;
        private bool _isMirrorX;
        private bool _isMirrorY;
        private double _scaleX;
        private double _scaleY;

        public double XOffset
        {
            get { return _xOffset; }
            set { _xOffset = value; }
        }
        
        public double YOffset
        {
            get { return _yOffset; }
            set { _yOffset = value; }
        }
        
        /// <summary>
        /// 以弧度表达的角度,以逆时针旋转为正.
        /// </summary>
        public double Rotation
        {
            get { return _rotation; }
            set { _rotation = value; }
        }
        
        public bool IsMirrorX
        {
            get { return _isMirrorX; }
            set { _isMirrorX = value; }
        }
        
        public bool IsMirrorY
        {
            get { return _isMirrorY; }
            set { _isMirrorY = value; }
        }
        
        public double ScaleX
        {
            get { return _scaleX; }
            set { _scaleX = value; }
        }
        

        public double ScaleY
        {
            get { return _scaleY; }
            set { _scaleY = value; }
        }

        //public Matrix GetMatrix( )
        //{
        //    double sinValue = Math.Sin(_rotation);
        //    double cosValue = Math.Cos(_rotation);


        //    float m11 = (float)((_isMirrorX ? -1 : 1) * _scaleX * cosValue);
        //    float m12 = (float)sinValue;

        //    float m21 = (float)(-sinValue);
        //    float m22 = (float)(( _isMirrorY ? -1 : 1) * _scaleY * cosValue); 

        //    float dx = (float)_xOffset;
        //    float dy = (float)_yOffset;


        //    Matrix m = new Matrix(m11, m12, m21, m22, dx, dy);
        //    return m;
        //}
    
    }
}
